#include <webby/message.hxx>
#include <iostream>

#include <sys/types.h>
#include <sys/socket.h>

#include <webby/server.hxx>
#include <webby/logging.hxx>

using namespace webby;

boost::shared_ptr< std::map< std::string, std::string > > Message::mMimeTypes;

Message::Message()
{
	if ( !mMimeTypes.get() ) 
	{
		mMimeTypes = boost::shared_ptr< std::map< std::string, std::string > >(new std::map< std::string, std::string >);
		mMimeTypes->operator[]("html") = "text/html";
		mMimeTypes->operator[]("ico") = "image/x-icon";
	}
}

void
ReadSocketMessage::doWork()
{
	char headerBuffer[1024*8];
	int amountRead = read( mSocket, headerBuffer, sizeof( headerBuffer ) );	
	if ( amountRead )
	{
		int i = 0;
		while ( i < amountRead && headerBuffer[i] != '\r' ) ++i;
		std::string requestLine( headerBuffer, i );
		std::vector< std::string > r = Message::parse( requestLine, " " );
		if ( r.size() == 3 )
		{
			mHeaders["REQUEST_TYPE"] = r[0];
			mHeaders["REQUEST_URI"] = r[1];
			mHeaders["REQUEST_PROTOCOL"] = r[2];
		}

		std::string msg( headerBuffer, amountRead );

		std::string key;
		std::string value;		
	
		bool onKey = true;
		bool begin = false;
		while ( i < amountRead )
		{
			if ( headerBuffer[i] == '\r') 
			{
				++i;
				continue;
			}

			if ( headerBuffer[i] == '\n') 
			{
				if ( !key.empty() )
				{
					mHeaders[key] = value;
				}

				key.clear();
				value.clear();
				onKey = true;
			} else if ( headerBuffer[i] == ':' && onKey ) 
			{
				onKey = false;
				begin = true;
			} else {
				if (onKey)
				{
					key = key + headerBuffer[i];
				} else {
					if (begin && headerBuffer[i] != ' ')
					{
						begin = false;
					}

					if ( !begin ) 
					{
						value = value + headerBuffer[i];
					}
				}
			}
			++i;
		}
	}

	handleRequest();
}

void
ReadSocketMessage::handleRequest()
{
	bool outputFile = false;
	if ( mHeaders["REQUEST_TYPE"] == "GET" )
	{
		outputFile = true;
	}
	
	{
		std::string document = "/var/www/html/duanestorey.com" + mHeaders["REQUEST_URI"];
		WEBBY_LOG( Logging::INFORMATIONAL, "Incoming " << mHeaders["REQUEST_TYPE"] << " request for " << document );
		std::string extension;
		std::string::size_type idx;
		idx = document.rfind('.');
		if(idx != std::string::npos)
		{
    			extension = document.substr(idx+1);
		}

		// check for fast CGI
		if ( mServerPtr->isFastCGI( extension ) )
		{
			//std::cout << "Handling Fast CGI Script" << std::endl;
			mServerPtr->addToLocalThreadQueue(  message_ptr( new BeginFastCGIMessage( mServerPtr,extension, document, mSocket ) ) );
			return;
		}

		FILE *f = fopen( document.c_str(), "rb" );
		if ( f ) 
		{
			fseek( f, 0L, SEEK_END );
			unsigned int fileSize = ftell( f );
			fseek( f, 0L, SEEK_SET );


			{
				outputHeaders( OK, getMIMETypeFromExtension( extension), fileSize );

				if ( outputFile ) {
					char buffer[8192];
					while ( !feof( f ) ) 
					{
						size_t amountRead = fread( buffer, 1, sizeof( buffer ), f );
						if ( amountRead )
						{
							send( mSocket, buffer, amountRead, 0 );
						}
					}
				}
			}

			fclose( f );
		} else {
			outputHeaders( NOTFOUND, "text/html", 0 );
		}
	}
	
	shutdown( mSocket, 1 );
	mServerPtr->handleSocketClose( mSocket );
}

std::vector< std::string > 
Message::parse( const std::string &str, const std::string &delimiter )
{
	std::vector< std::string > parsed;
	char *s = strdup( str.c_str() );
	char *val = strtok( s, delimiter.c_str() );
	while (true)
	{
		if ( !val )
		{
			break;
		}

		parsed.push_back( val );
		val = strtok( NULL, delimiter.c_str() );
	}
	free( s );
	return parsed;
}

void
Message::outputHeaders( Message::ResponseCode responseCode, const std::string &mimeType, unsigned int contentLength )
{
	WEBBY_LOG( Logging::INFORMATIONAL, "Sending HTTP/1.1 " << (unsigned int)responseCode << " response" );
	bool showContent = true;
	std::string headers;
	switch( responseCode )
	{
		case OK:
			headers = headers + "HTTP/1.1 200 Ok\r\n";
			break;
		case NOTFOUND: 
			headers = headers + "HTTP/1.1 404 Not Found\r\n";
			showContent = false;
			break;
	}

	headers = headers + "Server: Webby 1.0\r\n";

	if ( showContent )
	{
		char s[16];
		sprintf( s, "%d", contentLength );
		headers = headers + "Content-Length: " + s + "\r\n";
		headers = headers + "Content-Type: " + mimeType + "\r\n\r\n";
	}
	
	send( mSocket, headers.c_str(), headers.size(), 0 );

}

std::string 
ReadSocketMessage::getMIMETypeFromExtension( const std::string &extension )
{
	if ( extension == "html" )
	{
		return "text/html";
	} else return "text/html";
}

void
FastCGIOutputMessage::doWork()
{
	//std::cout << __FUNCTION__ << std::endl;
	int curSize = mContentSize;
	unsigned char *curPos = (unsigned char *)mContent;
	unsigned int length;
	std::string content;
	while (true)
	{
//		unsigned int version = curPos[0];
		unsigned int type = curPos[1];

		if (type == 3) break;

		length = (unsigned int)curPos[4];
		length = (length << 8);
		length = (length) + curPos[5];
		unsigned int padding = (((unsigned int)curPos[6]));

		content = content + std::string( (char *)&curPos[8], length );

		curSize -= (length + padding - 8);
		curPos += (8 + length + padding);

		if ( curSize <= 0) break;
	}



	std::string::size_type idx;
	idx = content.find("\r\n\r\n");

	if(idx != std::string::npos)
	{
		content = content.substr(idx+4);
	}

	outputHeaders( OK, "text/html", content.size() );
	send( mSocket, content.c_str(), content.size(), 0 );

	shutdown( mSocket, 1 );
	mServerPtr->handleSocketClose( mSocket );
	WEBBY_LOG( Logging::DEBUG, "Finished FastCGI procedure" );
	delete []mContent;
}

void 
BeginFastCGIMessage::doWork()
{
      mServerPtr->handleFastCGIScript( mExt, mScript, mSocket );
          
}
